#pragma once

#include <vector>
#include <string>
#include <stdint.h>

using std::string;

namespace Graph
{

class Vertex;
class Color;
class Matrix;
class Font;
class Direction;

typedef std::vector<Vertex> VertexVector;

/** @class Graphics
  * @brief Класс, реализующий возможность рисования примитивов.
  *
  * Абстрактный базовый класс для всех графических систем.
  */
class Graphics
{
public:

    /** @enum MatrixMode
      * @brief Тип матричного преобразования.
      *
      * Определеяет тип матричного преобразования.
      * UNDEFINED - соответсвует неопределенному значению MatrixMode.
      * MODELVIEW - идентифицирует матрицу модельно-видового преобразования.
      * PROJECTION - идентифицирует матрицу проекции.
      * TEXTURE - идентифицирует текстурную матрицу.
      * COLOR - идентифицирует цветовую матрицу.
      */
    enum MatrixMode {
        UNDEFINED = 0,
        MODELVIEW,
        PROJECTION,
        TEXTURE,
        COLOR
    };

    /**
      * Конструктор по умолчанию.
      */
    Graphics(){}

    /**
      * Очищает сцену устанавливая фоновый цвет по умолчанию (черный).
      */
    virtual void clear() = 0;

    /**
      * Устанавливает цвет, который будет установлен в буферах
      * после очистки. @see clear.
      *
      * @param color "фоновый" цвет, которым будет закрашена сцена
      *               после очистки.
      */
    virtual void clearColor(const Color &color) = 0;

    /**
      * Сбрасывает содержимое буферов.
      * Необходимо вызвать после рисования всех объектов.
      */
    virtual void flush() = 0;

    /**
      * � исует прямую линию между двумя точками.
      *
      * @param a начальная точка.
      * @param b конечная точка.
      */
    virtual void drawLine(const Vertex &a, const Vertex &b) = 0;

    /**
      * � исует ломаную по массиву точек.
      *
      * @param vertices массив точек. Если точек < 1 ничего не будет нарисовано.
      */
    virtual void drawPolyLine(const VertexVector &vertices) = 0;

    /**
      * � исует треугольник.
      *
      * @param a первая вершина треугольника.
      * @param b вторая вершина треугольника.
      * @param c третья вершина треугольника.
      */
    virtual void drawTriangle(const Vertex &a, const Vertex &b, const Vertex &c) = 0;

    /**
      * � исует множество треугольников.
      * Каждые 3 вершины задают 1 треугольник.
      *
      * @param vertices массив вершин. Если количество вершин n не кратно 3м,
      *                 последние 3 % n вершин будут проигнорированы.
      *                 Если вершин меньше, чем 3 ничего не будет нарисовано.
      */
    virtual void drawTriangles(const VertexVector &vertices) = 0;

    /**
      * � исует множетво треугольников, у каждого из которых
      * есть 2 общие вершины с соседними треугольниками.
      * Для первого треугольника используются вершины v0, v1, v2,
      * для второго v2, v1, v3, затем v2, v3, v4 и т.д.
      *
      * @param vertices массив вершин. Если вершин меньше, чем 3 ничего
      *                 не будет нарисовано.
      */
    virtual void drawTriangleStrip(const VertexVector &vertices) = 0;

    /**
      * � исует множество треугольников, имеющих 1 общую вершину.
      * Для первого треугольника используются вершины v0, v1, v2,
      * для второго v0, v2, v3, затем v0 v3, v4 и т.д.
      *
      * @param vertices массив вершин. Если вершин меньше, чем 3 ничего
      *                 не будет нарисовано.
      */
    virtual void drawTriangleFan(const VertexVector &vertices) = 0;

    /**
      * Отображает текст.
      *
      * @param text текст.
      * @param font шрифт.
      * @param size размер шрифта.
      * @param leftTop положение верхнего левого угла текста
      *                (в мировой системе координат).
      * @param direction направление текста.
      */
    virtual void drawText(const string &text, const Font &font, const uint8_t size,
                          const Vertex &leftTop, const Direction &direction) = 0;

    /**
      * Задает область просмотра.
      * @see http://msdn.microsoft.com/en-us/library/windows/desktop/bb174469(v=vs.85).aspx
      * @see http://msdn.microsoft.com/en-us/library/dd374202(v=vs.85).aspx
      *
      * @param x левый верхний угол прямоугольника области просмотра в пикселях.
      * @param y левый верхний угол прямоугольника области просмотра в пикселях.
      * @param width ширина прямоугольника области просмотра в пикселях.
      * @param height высота прямоугольника области просмотра в пикселях.
      */
    virtual void setViewport(int32_t x, int32_t y, int32_t width, int32_t height) = 0;

    /**
      * Применяет преобразование.
      * Текущая матрица преобразования умножается на матрицу
      * применяемого преобразования.
      *
      * @param matrix матрица применяемого преобразования.
      */
    virtual void applyTransformation(const Matrix &matrix) = 0;

    /**
      * Устанавливает текущую матрицу преобразований в единичную.
      * Матрица, над котоьрой производятся операции определяется значением
      * MatrixMode.
      * @see setMatrixMode.
      */
    virtual void resetTransformations() = 0;

    /**
      * Умножает текущую матрицу преобразований на матрицу масштабирования.
      *
      * @param scaleVertex вектор сдвига, компоненты которого определяют
      *                    изменение по соответствующей оси.
      */
    virtual void scale(const Vertex &scaleVertex) = 0;

    /**
      * Умножает текущую матрицу преобразований на матрицу сдвига.
      *
      * @param moveVertex вектор сдвига, компоненты которого определяют
      *                   изменение по соответствующей оси.
      */
    virtual void move(const Vertex &moveVertex) = 0;

    /**
      * Умножает текущую матрицу преобразований на матрицу поворота.
      *
      * @param axis вектор, вокруг которого совершается поворота.
      *             Не должен быть нулевым.
      * @param angle угол поворота.
      */
    virtual void rotate(const Vertex &axis, const float angle) = 0;

    /**
      * Возвращает текущую матрицу преобразований,
      * определяемую значением MatrixMode.
      */
    virtual const Matrix& getTransformation() const = 0;

    /**
      * Помещает текущую матрицу преобразования в стек.
      */
    virtual void pushTransformation() = 0;

    /**
      * Заменяет текущую матрицу преобразования матрицей
      * с вершины стека. Вершина стека удаляется из него.
      * Изначально в стеке всегда находится единичная матрица.
      * Стек не может быть пуст, при попытке удалить из стека
      * последнюю матрицу текущая матрица просто заменяется единичной.
      * Для каждой из матриц, определеямых MatrixMode
      * существует свой стек.
      *
      * @return новая текущая матрица преобразований.
      */
    virtual const Matrix& popTransformation() = 0;

    /**
      * Возврашает вершину стека матриц преобразований.
      * Для каждой из матриц, определеямых MatrixMode
      * существует свой стек.
      *
      * @return матрица преобразования на вершине стека.
      */
    virtual const Matrix& peekTransformation() const = 0;

    /**
      * Возвращает идентификатор выбранной матрицы,
      * над которой будут производиться последующие операции.
      * По умолчанию MODELVIEW.
      *
      * @return идентификатор.
      */
    virtual MatrixMode getMatrixMode() const = 0;


    /**
      * Устанавливает матрицу, над которой будут производиться
      * последующие операции.
      *
      * @param matrixMode идентификатор матрицы преобразований.
      *                   Если значение не соответствует ни одному из
      *                   значений MatrixMode, ничего не будет сделано.
      */
    virtual void setMatrixMode(const MatrixMode matrixMode) = 0;

    /**
      * Устанавливает единичную матрицу в качестве матрицы преобразрвания
      * для текущего значения MatrixMode.
      * @see setMatrixMode.
      */
    virtual void loadIdentity() = 0;

    /**
      * Умножает текущую матрицу на матрицу параллельного проецирования.
      * (ортогональную матрицу).
      * Матрица определяется значением MatrixMode.
      * @see setMatrixMode.
      * @see http://opengl.gamedev.ru/doc/?func=glOrtho.
      *
      * @param xLeft Координата левой вертикальной плоскости отсечения.
      * @param xRight Координата правой вертикальной плоскости отсечения.
      * @param yBottom Координата нижней горизонтальной плоскости отсечения.
      * @param yTop Координата верхней горизонтальной плоскости отсечения.
      * @param zNear Координата ближней плоскости отсечения по глубине.
      * @param zFar Координата дальней плоскости отсечения по глубине.
      */
    virtual void ortho(const float xLeft, const float xRight,
                       const float yBottom, const float yTop,
                       const float zNear, const float zFar) = 0;

    /**
      * Устанавливает толщину линии.
      * По умолчанию 1.0.
      * Толщина линии не поддерживается в DirectX.
      * Вызов метода для DirectX не имеет эффекта.
      *
      * @param lineWidth толщина линии.
      *
      * @deprecated толщина линии реализована с помощью костыля.
      */
    virtual void setLineWidth(const float lineWidth) = 0;

    /**
      * Возвращает текущую толщину линии.
      * По умолчанию 1.0.
      * Толщина линии не поддерживается в DirectX.
      * Вызов метода для DirectX всегда возвращает 1.0.
      *
      * @return толщина линии.
      *
      * @deprecated толщина линии реализована с помощью костыля.
      */
    virtual float getLineWidth() const = 0;

    /**
      * Возвращает 32х-битное представление цвета.
      * В зависимости от реализации, в формате RGBA или BGRA.
      *
      * @param color
      * @return 32х-битное представление цвета.
      */
    virtual uint32_t intColor(const Color &color) const = 0;

    /**
      * Возвращает 32х-битное представление цвета.
      * В зависимости от реализации, в формате RGBA или BGRA.
      *
      * @param r красная компонента цвета.
      * @param g зеленая компонента цвета.
      * @param b синяя компонента цвета.
      * @param a альфа компонента цвета.
      * @return 32х-битное представление цвета.
      */
    virtual uint32_t intColor(const uint8_t r, const uint8_t g,
                              const uint8_t b, const uint8_t a) const = 0;

    /**
      * Возвращает цвет, соответствующий заданному 32х-битному представлению.
      *
      * @param color 32х-битное представление цвета. @see intColor.
      * @return соответсвующий объект типа Color.
      */
    virtual Color fromIntColor(const uint32_t color) const = 0;

    /**
      * Деструктор.
      */
    virtual ~Graphics() {
    }
};

}

#if defined(_MSC_VER) && _MSC_VER >= 1600
#include <memory>
typedef std::shared_ptr<Graph::Graphics> GraphicsPtr;
#else
#include <tr1/memory>
typedef std::tr1::shared_ptr<Graph::Graphics> GraphicsPtr;
#endif
